package com.light.gulimall.ware.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.light.common.to.StockLockedTO;
import com.light.common.utils.PageUtils;
import com.light.common.utils.Query;
import com.light.common.utils.R;
import com.light.gulimall.ware.dao.WareInfoDao;
import com.light.gulimall.ware.dao.WareOrderTaskDao;
import com.light.gulimall.ware.dao.WareOrderTaskDetailDao;
import com.light.gulimall.ware.dao.WareSkuDao;
import com.light.gulimall.ware.entity.OrderEntity;
import com.light.gulimall.ware.entity.WareInfoEntity;
import com.light.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.light.gulimall.ware.entity.WareOrderTaskEntity;
import com.light.gulimall.ware.exception.NoStockException;
import com.light.gulimall.ware.feign.MemberRemotesServiceClient;
import com.light.gulimall.ware.feign.OrderRemotesServiceClient;
import com.light.gulimall.ware.service.WareInfoService;
import com.light.gulimall.ware.vo.CartItem;
import com.light.gulimall.ware.vo.FareVo;
import com.light.gulimall.ware.vo.MemberAddressVo;
import com.light.gulimall.ware.vo.StockLockVO;
import com.rabbitmq.client.Channel;
import io.seata.core.context.RootContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service("wareInfoService")
@RabbitListener(queues = "stock.release.stock.queue")
public class WareInfoServiceImpl extends ServiceImpl<WareInfoDao, WareInfoEntity> implements WareInfoService {

    @Autowired
    private MemberRemotesServiceClient memberRemotesServiceClient;
    @Autowired
    private WareSkuDao wareSkuDao;
    @Autowired
    private WareOrderTaskDao wareOrderTaskDao;
    @Autowired
    private WareOrderTaskDetailDao wareOrderTaskDetailDao;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    OrderRemotesServiceClient orderRemotesServiceClient;

    /**
     * 监听stock.release.stock.queue队列，进行库存解锁
     */
    @RabbitHandler
    public void stockRelease(StockLockedTO stockLockedTO, Message message, Channel channel) throws IOException {
        log.info("订单已关闭，准备被动解锁库存");
        Long id = stockLockedTO.getId();
        Long detailId = stockLockedTO.getDetailId();
        //首先用上面两个id查询wareOrderTaskDao和wareOrderTaskDetailDao
        //如果两张表都不存在，说明是锁库存的时候就报错了，不需要进行处理，订单也跟着回滚了
        WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskDao.selectById(id);
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = wareOrderTaskDetailDao.selectById(detailId);
        if (!ObjectUtils.isEmpty(wareOrderTaskDetailEntity) && !ObjectUtils.isEmpty(wareOrderTaskEntity)) {
            //根据orderSn查询订单表（远程调用订单服务），如果订单表不存在，说明是下单和扣库存之后又出现了异常，订单回滚了，这里也要解锁库存
            OrderEntity order = null;
            try {
                order = orderRemotesServiceClient.getOrder(wareOrderTaskEntity.getOrderSn());
            } catch (Exception e) {
                //远程调用失败，拒收消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
            }
            Long wareId = wareOrderTaskDetailEntity.getWareId();
            Long skuId = wareOrderTaskDetailEntity.getSkuId();
            Integer skuNum = wareOrderTaskDetailEntity.getSkuNum();
            if (ObjectUtils.isEmpty(order)) {
                //必须解锁库存
                this.doStockRelease(wareId, skuId, skuNum,detailId);
            }
            if (order.getStatus() == 4) {
                this.doStockRelease(wareId, skuId, skuNum,detailId);
            }
            //手动ack
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
        //  //如果两张表都不存在，说明是锁库存的时候就报错了，不需要进行处理，订单也跟着回滚了
        else {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }

    /**
     * 监听 关单后 主动发送给队列的消息
     * @param order
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitHandler
    public void stockRelease(OrderEntity order, Message message, Channel channel) throws IOException {
        log.info("订单已关闭，准备主动解锁库存");
        try {
            //再次判断工作单详情表的状态，筛选出是1的订单进行关单
            String orderSn = order.getOrderSn();
            WareOrderTaskEntity wareOrderTaskEntity = wareOrderTaskDao.selectOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderSn));
            Long id = wareOrderTaskEntity.getId();
            List<WareOrderTaskDetailEntity> wareOrderTaskDetailEntities = wareOrderTaskDetailDao.selectList(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id", id));
            List<WareOrderTaskDetailEntity> collect = wareOrderTaskDetailEntities.stream().filter(wareOrderTaskDetailEntity -> wareOrderTaskDetailEntity.getLockStatus() == 1).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                for (WareOrderTaskDetailEntity wareOrderTaskDetailEntity : collect) {
                    //解锁
                    doStockRelease(wareOrderTaskDetailEntity.getWareId(), wareOrderTaskDetailEntity.getSkuId(), wareOrderTaskDetailEntity.getSkuNum(),id);
                }
            }
            //手动确认
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            //重新放回队列
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }
    }

    //wms_ware_sku表
    private void doStockRelease(Long wareId, Long skuId, Integer skuNum, Long detailId) {
        wareSkuDao.stockRelease(wareId, skuId, skuNum);
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(detailId);
        wareOrderTaskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailDao.updateById(wareOrderTaskDetailEntity);
    }

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<WareInfoEntity> page = this.page(
                new Query<WareInfoEntity>().getPage(params),
                new QueryWrapper<WareInfoEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 根据条件查询仓库列表
     *
     * @param params 查询条件
     * @return 组装分页参数
     */
    @Override
    public PageUtils queryByCondition(Map<String, Object> params) {

        QueryWrapper<WareInfoEntity> wareInfoEntityQueryWrapper = new QueryWrapper<>();

        String key = (String) params.get("key");
        if (!StringUtils.isBlank(key)) {
            wareInfoEntityQueryWrapper.and((w) -> {
                w.eq("id", key)
                        .or().like("name", key)
                        .or().like("address", key)
                        .or().like("areacode", key);
            });
        }

        IPage<WareInfoEntity> page = this.page(
                new Query<WareInfoEntity>().getPage(params),
                wareInfoEntityQueryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public FareVo getFare(Long addrId) {
        FareVo fareVo = new FareVo();
        R r = memberRemotesServiceClient.info(addrId);
        LinkedHashMap memberReceiveAddress = (LinkedHashMap) r.get("memberReceiveAddress");
        ObjectMapper objectMapper = new ObjectMapper();
        MemberAddressVo data = objectMapper.convertValue(memberReceiveAddress, MemberAddressVo.class);
        if (data != null) {
            // 简单处理：截取手机号最后一位作为邮费
            String phone = data.getPhone();
            String substring = phone.substring(phone.length() - 1, phone.length());
            BigDecimal bigDecimal = new BigDecimal(substring);
            fareVo.setAddressVo(data);
            fareVo.setFare(bigDecimal);
            return fareVo;
        }
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void stockNum(StockLockVO vo) {
        log.info("Seata全局事务id=================>{}", RootContext.getXID());
        String orderSn = vo.getOrderSn();
        List<CartItem> cartItems = vo.getCartItems();
        ArrayList<HasStock> hasStocks = new ArrayList<>();

        //锁定库存之前先创建一个工作单
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(orderSn);
        wareOrderTaskDao.insert(wareOrderTaskEntity);

        for (CartItem cartItem : cartItems) {
            HasStock hasStock = new HasStock();
            Long skuId = cartItem.getSkuId();
            //找到这个skuId在哪个仓库有库存
            List<Long> wareIds = wareSkuDao.selectWareIdBySkuId(skuId);
            hasStock.setSkuId(skuId);
            hasStock.setWareIds(wareIds);
            hasStock.setCount(cartItem.getCount());
            hasStocks.add(hasStock);
        }

        for (HasStock hasStock : hasStocks) {
            boolean singleLocked = false;
            Long skuId = hasStock.getSkuId();
            Integer count = hasStock.getCount();
            List<Long> wareIds = hasStock.getWareIds();
            if (CollectionUtils.isEmpty(wareIds)) {
                throw new NoStockException(skuId);
            }
            //依次锁定库存，这个仓库库存锁定完了，还需要继续锁定，就锁定下一个仓库的
            for (Long wareId : wareIds) {
                int resCount = wareSkuDao.stockLock(skuId, wareId, count);
                if (resCount > 0) {
                    singleLocked = true;
                    //锁定成功，创建库存工作单详情
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setWareId(wareId);
                    wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    wareOrderTaskDetailEntity.setSkuNum(hasStock.getCount());
                    wareOrderTaskDetailEntity.setSkuId(skuId);
                    wareOrderTaskDetailEntity.setLockStatus(1);
                    wareOrderTaskDetailDao.insert(wareOrderTaskDetailEntity);
                    //向消息队列发送延迟消息 用于后续判断是否需要解锁库存
                    StockLockedTO stockLockedTO = new StockLockedTO();
                    stockLockedTO.setId(wareOrderTaskEntity.getId());
                    stockLockedTO.setDetailId(wareOrderTaskDetailEntity.getId());
                    rabbitTemplate.convertAndSend("stock-event-exchange", "stock.locked", stockLockedTO);
                    break;
                }
            }
            //某个skuId所有仓库都没有锁住
            if (!singleLocked) {
                throw new NoStockException(skuId);
            }
        }
    }

    @Data
    class HasStock {

        private Long skuId;

        private List<Long> wareIds;

        private Integer count;
    }

}